/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2019 Ha Thach (tinyusb.org)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "tusb.h"

#include "usb_descriptors.h"

#include "mpssed_io_def.h"
#include "mpssed.h"

//#include "DAP.h"
//#include "DAP_queue.h"

static volatile uint8_t  USB_ResponseIdle;;

enum {
    USBD_HID_REQ_EP_CTRL = 0,             /* Request from control endpoint      */
    USBD_HID_REQ_EP_INT,                  /* Request from interrupt endpoint    */
    USBD_HID_REQ_PERIOD_UPDATE            /* Request from periodic update       */
};

//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+

void hid_task(void);

/*------------- MAIN -------------*/
int hid_main(void)
{

  // init device stack on configured roothub port
  //USB_ResponseIdle = 1;
  //DAP_queue_init(&DAP_Cmd_queue);

  //初始化MSB
  int i=0;
  for(i=0;i<0x100;i++){
    byteLSB[i] = i;
    byteMSB[i] =
      (((i>>7)&1)<<0)|(((i>>3)&1)<<4)|
      (((i>>6)&1)<<1)|(((i>>2)&1)<<5)|
      (((i>>5)&1)<<2)|(((i>>1)&1)<<6)|
      (((i>>4)&1)<<3)|(((i>>0)&1)<<7);
  }

  //初始化GPIO
  crm_periph_clock_enable(CRM_TMR3_PERIPH_CLOCK, TRUE);
  crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
  crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
  crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE);
  tmr_counter_enable(TMR3,TRUE);

  u8 cmd[] = {0x80,0x8,0xb};    //设置JTAG初始化
  MPSSED_ProcessCmd(cmd,3);

  tud_init(0);

  while (1)
  {
    tud_task(); // tinyusb device task

    hid_task();
  }
}

//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+

// Invoked when device is mounted
void tud_mount_cb(void)
{
  printf("mount cb:!\n");
}

// Invoked when device is unmounted
void tud_umount_cb(void)
{
  printf("umount cb:!\n");
}

// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us  to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
  (void) remote_wakeup_en;
  printf("tud_suspend_cb:!\n");
}

// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
  printf("tud_resume_cb:!\n");
}

//--------------------------------------------------------------------+
// USB HID
//--------------------------------------------------------------------+

static void send_hid_report(uint8_t report_id, uint32_t btn)
{
  // skip if hid is not ready yet
  if ( !tud_hid_ready() ) return;

  switch(report_id)
  {
    case REPORT_ID_KEYBOARD:
    {
      // use to avoid send multiple consecutive zero report for keyboard
      static bool has_keyboard_key = false;

      if ( btn )
      {
        uint8_t keycode[6] = { 0 };
        keycode[0] = HID_KEY_A;

        //tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keycode);
        has_keyboard_key = true;
      }else
      {
        // send empty key report if previously has key pressed
        if (has_keyboard_key) tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL);
        has_keyboard_key = false;
      }
    }
    break;

    case REPORT_ID_MOUSE:
    {
      int8_t const delta = 5;

      // no button, right + down, no scroll, no pan
      tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, delta, delta, 0, 0);
    }
    break;

    case REPORT_ID_CONSUMER_CONTROL:
    {
      // use to avoid send multiple consecutive zero report
      static bool has_consumer_key = false;

      if ( btn )
      {
        // volume down
        uint16_t volume_down = HID_USAGE_CONSUMER_VOLUME_DECREMENT;
        tud_hid_report(REPORT_ID_CONSUMER_CONTROL, &volume_down, 2);
        has_consumer_key = true;
      }else
      {
        // send empty key report (release key) if previously has key pressed
        uint16_t empty_key = 0;
        if (has_consumer_key) tud_hid_report(REPORT_ID_CONSUMER_CONTROL, &empty_key, 2);
        has_consumer_key = false;
      }
    }
    break;

    case REPORT_ID_GAMEPAD:
    {
      // use to avoid send multiple consecutive zero report for keyboard
      static bool has_gamepad_key = false;

      hid_gamepad_report_t report =
      {
        .x   = 0, .y = 0, .z = 0, .rz = 0, .rx = 0, .ry = 0,
        .hat = 0, .buttons = 0
      };

      if ( btn )
      {
        report.hat = GAMEPAD_HAT_UP;
        report.buttons = GAMEPAD_BUTTON_A;
        tud_hid_report(REPORT_ID_GAMEPAD, &report, sizeof(report));

        has_gamepad_key = true;
      }else
      {
        report.hat = GAMEPAD_HAT_CENTERED;
        report.buttons = 0;
        if (has_gamepad_key) tud_hid_report(REPORT_ID_GAMEPAD, &report, sizeof(report));
        has_gamepad_key = false;
      }
    }
    break;

    default: break;
  }
}

uint16_t vendor_array[] = {
  0x0808,
  0x0403,
  0x6010,
  0x0500,
  0xfa80,
  0x0000,
  0x0200,
  0x0e96,
  0x1aa6,
  0x0000,

  'F',
  'T',
  '2',
  '2',
  '3',
  '2',
  0x030a,
  'J',
  'T',
  'A',
  'G',
  0x0000,
  'J',
  'T',
  'A',
  'G',
  0x0000,
  0x0000,
  0x0000,
  0x0000,
  0x0000,
  0x0000,

  0x0000,
  0x1027,
  0x6010,
};

bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);

bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request){
  if (stage != CONTROL_STAGE_SETUP) return true;
  if((request->bmRequestType&0xf0)==0xc0){
    if(request->wIndex<0x20){
      return tud_control_xfer(rhport,request,&vendor_array[request->wIndex],request->wLength);
    }
    else if(request->wIndex==0x3f){
      return tud_control_xfer(rhport,request,&vendor_array[0x21],request->wLength);
    }
    else if(request->wIndex<0x3f){
      return tud_control_xfer(rhport,request,&vendor_array[0x20],request->wLength);
    }
  }
  else{
    //return tud_control_xfer(rhport,request,&vendor_array[0x22],2);
    //tud_vendor_n_write(1,&vendor_array[0x22],2);
	  dcd_edpt_xfer(rhport,0x83,&vendor_array[0x22],2);
  }
  return tud_control_status(rhport, request);
}

u8 byteMSB[0x100];
u8 byteLSB[0x100];
/*发送的缓存*/
u8 sendBuf[1024] = {0};
int sendBuf_write_pos = 0;
int sendBuf_pos = 0;
u8 cmd_len[0x100];
int CLK_SET_V = 1<<TCK_PIN_Bit;
int CLK_CLR_V = 1<<TCK_PIN_Bit<<16;
int loopback = 0;
int div5 = 5;
int period;
int send_len = 0;
int send_usb(u8*data,int len);
u8 usb_send_buf[64];      //需要加个头
int send_start = 0;

int _send_usb_(){
  if(send_start){
    /*需要0x87命令才开始发送数据*/
    memcpy(usb_send_buf+2,sendBuf + sendBuf_pos,62);   //64-2大小的数据缓存

    usb_send_buf[0] = 0x01; usb_send_buf[1] = 0x60;   //设置数据头

    int send_len = sendBuf_write_pos - sendBuf_pos;
    int len = send_len + 2;
    if(len>64)len = 64;
    int ret = tud_vendor_write(usb_send_buf,len);
    tud_vendor_flush();

    ret -= 2;   //
    if(ret>0){

      if(ret>=send_len){
        //全部发送完成
        sendBuf_pos = 0;
        sendBuf_write_pos = 0;
        send_start = 0;     //关闭发送数据
      }
      else{
        //只发送了一部分
        sendBuf_pos += ret;         //设置下一份发送数据
      }
    }
  }
  return 0;
}

int send_usb_proc(){
  send_start = 1;
  return _send_usb_();
}

int send_usb(u8*data,int len){
  //应该写入缓存,处理完命令后再发送数据
  sendBuf_write_pos += len;
  return 0;
}

int send_err(u8 cmd){
  usb_send_buf[0] = 0xfa;
  usb_send_buf[1] = cmd;
  int ret = tud_vendor_write(usb_send_buf,2);
  tud_vendor_flush();
  return ret;
}

static void check_send_buf(){
	if(sendBuf_write_pos!=0){
		_send_usb_();
	}
}

/*需要发送其他缓存*/
void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes){
	check_send_buf();
}


/*有时候缓存太长.需要额外接收数据*/
u8 cmd_buf[1024];
int cmd_buf_pos = 0;
int cmd_buf_read_pos = 0;
void tud_vendor_rx_cb(uint8_t itf){
  //读取64个字节
  int cmd_len = tud_vendor_read(cmd_buf + cmd_buf_read_pos,64);
  cmd_buf_read_pos += cmd_len;
  //u8 send_buf[64] = {0xFA,0x00};
  //tud_vendor_write(send_buf,64);
  //dcd_edpt_xfer(0,0x81,send_buf,2);
  //USB_ResponseIdle = c;
  cmd_buf_pos;
  while(1){
    int ret = MPSSED_ProcessCmd(cmd_buf+cmd_buf_pos,cmd_buf_read_pos-cmd_buf_pos);


    if(ret<=0){
      //错误的命令

      //数据太长.需要等待下一个包
    	check_send_buf();
    	return;
    }
    cmd_buf_pos += ret;
    if(cmd_buf_read_pos-cmd_buf_pos<=0){
      //结束处理所有命令
      cmd_buf_read_pos = 0;
      cmd_buf_pos = 0;
      break;
    }
  }
  check_send_buf();

}

// Every 10ms, we will sent 1 report for each HID profile (keyboard, mouse etc ..)
// tud_hid_report_complete_cb() is used to send the next report after previous one is complete
void hid_task(void)
{
  // Poll every 10ms
  const uint32_t interval_ms = 10;
  static uint32_t start_ms = 0;

  start_ms += interval_ms;

  if(tud_vendor_available()){
      uint8_t buf[64];
      uint32_t count = tud_vendor_read(buf, sizeof(buf));
  }

  uint32_t const btn = 0;// board_button_read();

  // Remote wakeup
  if ( tud_suspended() && btn )
  {
  }else
  {
  }
}

static uint16_t hid_get_report(u8 report_type,u8 rid,u8*buffer,u8 req);
// Invoked when sent REPORT successfully to host
// Application can use this to send the next report
// Note: For composite reports, report[0] is report ID
void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len)
{
  (void) instance;
  (void) len;

}


static uint16_t hid_get_report(u8 report_type,u8 rid,u8*buffer,u8 req){
    uint8_t * sbuf;
    int slen;

    return 0;
}

// Invoked when received GET_REPORT control request
// Application must fill buffer report's content and return its length.
// Return zero will cause the stack to STALL request
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
{
  // TODO not Implemented
  (void) instance;
  (void) report_id;
  (void) report_type;
  (void) buffer;
  (void) reqlen;

  return hid_get_report(report_type,report_id,buffer,USBD_HID_REQ_EP_CTRL);
}

int hid_send_packet()
{
    return 1;
}

// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
{
  (void) instance;

  uint8_t *rbuf;
}
